Clock Controller

Descripcion

Controlador AVR para reloj hecho con led matrix de 8x8

El circuito del controlador en Kicad es el siguiente: Clock controller

El circuito del led matrix en Kicad es el siguiente: Led Matrix 74HC595

Codigo
#include <avr/io.h>
#include <avr/power.h>

#include <util/delay.h>
#include <avr/interrupt.h>

#include "digits.h"

#define LATCH_PIN PB2  // ST_CP

#define DISPLAY_COLUMNS 8
#define DISPLAY_ROWS 8
#define CHAR_COLUMNS 8
#define NUMBER_OF_DISPLAYS 1
#define SLIDE_SPEED_DELAY 60

#define TIME_CALIBRATION -4


volatile uint16_t time_counter = 0;
volatile uint16_t time2_counter = 0;

volatile uint8_t display[NUMBER_OF_DISPLAYS][DISPLAY_ROWS];
volatile uint8_t hour_1;
volatile uint8_t hour_2;
volatile uint8_t hour_1;
volatile uint8_t min_1;
volatile uint8_t min_2;

volatile uint8_t min_1_animation = 0;
volatile uint8_t min_2_animation = 0;
volatile uint8_t hour_1_animation = 0;
volatile uint8_t hour_2_animation = 0;
volatile uint8_t animation_counter = 0;


volatile uint8_t display_hour_1[DISPLAY_ROWS];
volatile uint8_t display_hour_2[DISPLAY_ROWS];
volatile uint8_t display_min_1[DISPLAY_ROWS];
volatile uint8_t display_min_2[DISPLAY_ROWS];
volatile uint8_t display_seconds[DISPLAY_ROWS];
volatile uint8_t seconds=0;
volatile uint8_t minutes = 0;
volatile uint8_t hours = 0;
volatile uint8_t count = 0;

void SPI_init(void) {
    // Configurar MOSI, SCK y LATCH como salida
    DDRB |= (1 << PB3) | (1 << PB5) | (1 << LATCH_PIN);

    // Configurar SPI como Master
    SPCR = (1 << SPE) | (1 << MSTR);
}

void SPI_send(uint8_t data) {
    SPDR = data;                     // Cargar dato en el registro de SPI
    while (!(SPSR & (1 << SPIF)));   // Esperar que termine la transmisión
}

void latch() {
    // Pulso en LATCH para actualizar salidas
    PORTB |= (1 << LATCH_PIN);
    //_delay_us(1); // Opcional? descomentar en caso de que el latch no funcione
    PORTB &= ~(1 << LATCH_PIN);
}

void increase_seconds(){

  display_seconds[seconds/DISPLAY_ROWS] |= (1 << (seconds%8));
  seconds++;

}

ISR(TIMER0_COMPA_vect) {


    SPI_send(display_hour_1[count]);
    SPI_send(display_hour_2[count]);

    SPI_send(display_min_1[count]);
    SPI_send(display_min_2[count]);

    SPI_send(display_seconds[count]);

    SPI_send(~(1 << count));

    latch();

    count++;

    if(count >= DISPLAY_ROWS)
        count=0;
}

ISR(TIMER1_COMPA_vect) {
    time_counter += 64;

    if(time_counter >= 1000){
      time_counter -= 1000;
      increase_seconds();

      if(seconds >= 60){
        seconds = 0;
        for(uint8_t i = 0; i < 8; i++)
          display_seconds[i] = 0;

        minutes++;

        min_2 = minutes%10;
        min_1 = (minutes/10)%10;

        min_2_animation = 1;

        if(minutes%10 == 0)
          min_1_animation = 1;

      }

      if(minutes >= 60){
        minutes = 0;
        min_2 = minutes%10;
        min_1 = (minutes/10)%10;

        hours++;

        hour_2 = hours%10;
        hour_1 = (hours/10)%10;

        hour_2_animation = 1;
        if(hours%10 == 0)
          hour_1_animation = 1;
      }

      if(hours >= 24){
        hour_2 = 0;
        hour_1 = 0;

        hours = 0;

        hour_1_animation = 1;
      }
    }
}

void update_animation(volatile uint8_t *display, uint8_t number){
  for(uint8_t i = 0; i < 7; i++)
    display[i] = display[i+1];

  display[7] = digits[number][animation_counter];
}

void disable_animations(){
  hour_1_animation = 0;
  hour_2_animation = 0;

  min_1_animation = 0;
  min_2_animation = 0;

}

ISR(TIMER2_COMPA_vect) {

  time2_counter++;

  if(time2_counter >= 3){
    time2_counter = 0;

    if(min_2_animation){
      update_animation(display_min_2, min_2);
    }

    if(min_1_animation){
      update_animation(display_min_1, min_1);
    }


    if(hour_2_animation){
      update_animation(display_hour_2, hour_2);
    }

    if(hour_1_animation){
      update_animation(display_hour_1, hour_1);
    }

    if(min_1_animation || min_2_animation){
      animation_counter++;
    }

    if(animation_counter == 8){
      animation_counter = 0;

      disable_animations();
    }
  }
}


void init_timer0(){
  TCCR0A |= (1 << WGM01);
  TCCR0B |= (1 << CS02); // Prescaler de 256

  TIMSK0 |= (1 << OCIE0A);

  OCR0A = 2; // Interrupción cada 512 microsegundos

}

void init_timer1() {
    TCCR1B |= (1 << WGM12);                // Modo CTC
    OCR1A = 64000 + TIME_CALIBRATION;
    TCCR1B |= (1 << CS11);   // Prescaler 8
    TIMSK1 |= (1 << OCIE1A); // Habilitar interrupción por OCR1A
}

void init_timer2(){
  TCCR2A |= (1 << WGM21); // modo CTC
  TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20); // Clock with /1024 prescaler

  TIMSK2 |= (1 << OCIE2A); // Habilitar interrupción por OCR2A

  OCR2A = 250;

}

void set_zero_time(volatile uint8_t *display){
  for(uint8_t i = 0; i < 8; i++)
      display[i] = digits[0][i];
}

ISR(PCINT1_vect) {
  if(bit_is_clear(PINC, PC0)){
    seconds = 0;
    time_counter = 0;

    for(uint8_t i = 0; i < 8; i++)
      display_seconds[i] = 0;

    TCNT1 = 0;

  }
  else if(bit_is_clear(PINC, PC1)){
    minutes++;

    if(minutes >= 60)
      minutes = 0;

    min_2 = minutes%10;
    min_1 = (minutes/10)%10;

    for(uint8_t i = 0; i < 8; i++){
      display_min_1[i] = digits[min_1][i];
      display_min_2[i] = digits[min_2][i];
    }

  }
  else if(bit_is_clear(PINC, PC2)){
    hours++;

    if(hours >= 24)
      hours = 0;

    hour_2 = hours%10;
    hour_1 = (hours/10)%10;

    for(uint8_t i = 0; i < 8; i++){
      display_hour_1[i] = digits[hour_1][i];
      display_hour_2[i] = digits[hour_2][i];
    }

  }

  _delay_ms(4);
  PCIFR |= (1 << PCIF1);
}

void init_button_interrupts(){
  PCICR |= (1 << PCIE1);

  PCMSK1 |= (1 << PCINT8) | (1 << PCINT9) | (1 << PCINT10);
}

int main(void) {
    clock_prescale_set(clock_div_1);

    SPI_init();

    PORTC = 0b00000111;

    init_timer0();
    init_timer1();
    init_timer2();

    init_button_interrupts();

    sei();

    set_zero_time(display_min_1);
    set_zero_time(display_min_2);
    set_zero_time(display_hour_1);
    set_zero_time(display_hour_2);

    while (1) {

    }
}

digits.h file:

/* digits_h_inverted.h - 0..9, cada número 8 bytes, LSB = columna izquierda */
#include <stdint.h>

volatile uint8_t digits[][8] =
{
  // 0
  {
    0b00111100,
    0b01000010,
    0b01100010,
    0b01010010,
    0b01001010,
    0b01000110,
    0b00111100,
    0b00000000,
  },
  // 1
  {
    0b00010000,
    0b00011000,
    0b00010100,
    0b00010000,
    0b00010000,
    0b00010000,
    0b00111100,
    0b00000000,
  },
  // 2
  {
    0b00111100,
    0b01000010,
    0b01000000,
    0b00110000,
    0b00001000,
    0b00000100,
    0b01111110,
    0b00000000,
  },
  // 3
  {
    0b00111100,
    0b01000010,
    0b01000000,
    0b00111000,
    0b01000000,
    0b01000010,
    0b00111100,
    0b00000000,
  },
  // 4
  {
    0b00100000,
    0b00110000,
    0b00101000,
    0b00100100,
    0b01111110,
    0b00100000,
    0b00100000,
    0b00000000,
  },
  // 5
  {
    0b01111110,
    0b00000010,
    0b00111110,
    0b01000000,
    0b01000000,
    0b01000010,
    0b00111100,
    0b00000000,
  },
  // 6
  {
    0b00111100,
    0b01000010,
    0b00000010,
    0b00111110,
    0b01000010,
    0b01000010,
    0b00111100,
    0b00000000,
  },
  // 7
  {
    0b01111110,
    0b01000000,
    0b00100000,
    0b00010000,
    0b00001000,
    0b00001000,
    0b00001000,
    0b00000000,
  },
  // 8
  {
    0b00111100,
    0b01000010,
    0b01000010,
    0b00111100,
    0b01000010,
    0b01000010,
    0b00111100,
    0b00000000,
  },
  // 9
  {
    0b00111100,
    0b01000010,
    0b01000010,
    0b01111100,
    0b01000000,
    0b01000010,
    0b00111100,
    0b00000000,
  },
};
Circuito simulador

Circuito simulIDE: Clock controller

Tags

AVR | microcontrolador | clock | controller | led matrix